<?php defined('SYSPATH') or die('No direct script access.');

class Application_Queue_Controller extends Controller
{
	protected $sigterm = FALSE;
	protected $sighup  = FALSE;
	protected $pids = array();

	protected $connection = NULL;

	public function __construct()
	{
		parent::__construct();
	}

	protected function connect()
	{
		$this->connection = mysql_connect(Config::item('database.queue.connection.host'), Config::item('database.queue.connection.user'), Config::item('database.queue.connection.pass'), TRUE);
		mysql_select_db('analytics_queue', $this->connection);
	}

	public function run()
	{
		ini_set("max_execution_time", "0");
		ini_set("max_input_time", "0");
		set_time_limit(0);

		pcntl_signal(SIGCHLD, array($this, 'sig_handler'));
		pcntl_signal(SIGTERM, array($this, 'sig_handler'));
		pcntl_signal(SIGHUP, array($this, 'sig_handler'));
		declare(ticks = 1);

		$pid = pcntl_fork(); // fork
		if ($pid == -1)
		{
			Log::add('error', 'Could not fork.');
			exit;
		}
		else if ($pid) // parent
		{
			Log::add('debug', 'Forked Once');
			exit;
		}
		else
		{
			// Write the log to ensure no memory issues
			Log::write();
			$pid = pcntl_fork();

			if($pid == -1)
			{
				Log::add('error', 'Could not fork.');
				exit;
			}
			elseif($pid) // Parent
			{
				// We don't do anything
				Log::add('error', 'Parent.');
			}
			else // We are the final daemon
			{
				Log::add('debug', 'Starting the scanning');
				$task_model = new Task_Model();

				while ( ! $this->sigterm)
				{
					// Only scan based on our config item
					usleep(Config::item('queue.scan_delay'));
					$this->connect();
					if ($this->sighup)
					{
						// We need to reload the config
						Config::clear('queue');
						Config::clear('database');
						Config::clear('log');
						Config::clear('routes');
						Config::clear('core');
						$this->sighup = FALSE;
					}

					$sql = 'SELECT * FROM `'.Config::item('queue.table_name').'` ORDER BY `priority` DESC LIMIT 1';
					$result = mysql_query($sql, $this->connection);
					$num_rows = is_resource($result) ? mysql_num_rows($result) : 0;

					// Scan the queue
					if ((count($this->pids) < Config::item('queue.parallel_operations')) AND ($num_rows > 0))
					{
						// Write the log to ensure no memory issues
						Log::write();
						$pid = pcntl_fork();
						if($pid == -1)
						{
							Log::add('error', 'Could not spawn child task process.');
							exit;
						}
						elseif($pid) // Parent
						{
							// Add the child's PID to the running list
							$this->pids[$pid] = time();
						}
						else // We are the spawned child to perform a task in the background
						{
							$queue = new Task_Model;
							$queue->scan()->execute();
							exit;
						}
					}
					mysql_free_result($result);
					mysql_close($this->connection);
				}

				// Run cleanup stuffs
				$this->cleanup();
			}
		}
	}

	protected function cleanup($tries = 0)
	{
		while (($tries++ < 5) AND $this->kill_all_children());
		echo 'Exiting!';
		if (count($this->pids))
			Log::add('info', 'Could not kill all children in 5 tries...');
	}

	protected function kill_all_children()
	{
		foreach ($this->pids as $pid => $time_started)
		{
			posix_kill($pid, SIGTERM);
			usleep(500);
		}

		return (count($this->pids) == 0);
	}

	public function sig_handler($signo)
	{
		switch ($signo)
		{
			case SIGCHLD: // This fires when a child dies. We grab the PID of the dying child and remove it from the list
				while( ($pid = pcntl_wait($status, WNOHANG || WUNTRACED)) > 0)
					unset($this->pids[$pid]);
				break;
			case SIGTERM:
				$this->sigterm = TRUE;
				Log::add('debug', 'Hit a SIGTERM');
				break;
			case SIGHUP:
				$this->sighup = TRUE;
				Log::add('debug', 'Hit a SIGHUP');
				break;
		}
	}



	// Use Case Methods

	public function index()
	{
		/*for ($i = 0; $i <= 10; $i++)
		{
			$value = rand(0, 11);
			if ($task_queue->add('Application_Queue_Controller', 'test', array('value' => $value), $value))
				echo '<p>Task added to the queue</p>';
			else
				echo '<p>Could not add the task to the queue</p>';
		}*/
		$this->profiler = new Profiler();

		for ($i = 0; $i <= 20; $i++)
		{
			$value = rand(0, 11);
			if (Task_Queue::add('sleep', array('value' => mt_rand(0, 11))))
				echo '<p>Task added to the queue</p>';
			else
				echo '<p>Could not add the task to the queue</p>';
		}
	}

	public function test($value)
	{
		//die(Kohana::debug($value));
		// This just inserts a test row in the test table
		$this->db->insert('test', array('value' => $value));
	}

	public function sleep($value)
	{
		$start = time();
		sleep($value);
		$end = time();
		Log::add('info', 'Slept for '.$value.' seconds. Started at '.$start.' and ended at '.$end);
	}
}